home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Devices / Apple Desktop Bus / ADB Key Spy / Sources / ADB Key Spy.c < prev    next >
Encoding:
Text File  |  1995-09-20  |  6.7 KB  |  322 lines  |  [TEXT/MMCC]

  1.     //
  2.     //    ADB Key Spy • Pete Gontier • gurgle@apple.com
  3.     //    Macintosh Developer Technical Support
  4.     //    © 1995 Apple Computer, Inc.
  5.     //
  6.     //    See "ADB Key Spy.h" for more information.
  7.     //
  8.     //    To do:
  9.     //
  10.     //        API to interrogate keyboards one-by-one
  11.     //
  12.     //    Changes:
  13.     //
  14.     //        when        who        what
  15.     //        --------------------------------------------------------------
  16.     //        08/23/95    PG        history begins
  17.     //        08/24/95    PG        service routines split off into separate
  18.     //                            code resource for use with PowerPC
  19.     //        09/11/95    PG        first cut at virtual key map support
  20.     //
  21.  
  22. #ifndef __MEMORY__
  23. #    include <Memory.h>
  24. #endif
  25.  
  26. #ifndef __ERRORS__
  27. #    include <Errors.h>
  28. #endif
  29.  
  30. #ifndef __LOWMEM__
  31. #    include <LowMem.h>
  32. #endif
  33.  
  34. #include "ADB Key Spy.h"
  35.  
  36.     //
  37.     //    gKeyboardInfoList
  38.     //
  39.     //    This is the head of the linked list mentioned above.
  40.     //
  41.  
  42. static tKeyboardInfo *gKeyboardInfoList;
  43.  
  44.     //
  45.     //    gServiceRoutine
  46.     //
  47.     //    This is the detached resource that contains the 68K code
  48.     //    which implements the ADB service routine.
  49.     //
  50.  
  51. static Handle gServiceRoutine;
  52.  
  53.     //
  54.     //    GetKMAP
  55.     //
  56.     //    The 'KMAP' resources map raw key codes to virtual key codes.
  57.     //    To make it possible for the service routine to easily perform
  58.     //    this mapping, we toos a handle to the appropriate 'KMAP'
  59.     //    resource into each keyboard info record. The resource is
  60.     //    marked locked, so it's interrupt-safe.
  61.     //
  62.  
  63. static pascal OSErr GetKMAP (tKeyboardInfo *kbInfo, char devType)
  64. {
  65.     //
  66.     //    One is not supposed to release resources which potentially come from
  67.     //    the System file because they might be shared. Thus, we get our resource
  68.     //    and keep it ("leak" it). If you have 'KMAP' resources in some other file
  69.     //    for some reason, then you might want to call HomeResFile here to help
  70.     //    you decide whether to release the resource.
  71.     //
  72.  
  73.     OSErr err = noErr;
  74.  
  75.     const ResType kmapResType = 'KMAP';
  76.  
  77.     tKeyMapResourceH kmapH = (tKeyMapResourceH) GetResource (kmapResType, devType);
  78.     err = ResError ( );
  79.     if (err == resNotFound || !kmapH)
  80.     {
  81.         kmapH = (tKeyMapResourceH) GetResource (kmapResType, 0);
  82.         err = ResError ( );
  83.         if (err == resNotFound)
  84.         {
  85.             err = noErr;
  86.             kmapH = nil; // just to be sure
  87.         }
  88.     }
  89.  
  90.     kbInfo->keyMapResH = kmapH;
  91.  
  92.     return err;
  93. }
  94.  
  95.     //
  96.     //    AKS_IsKeyDownAnywhere
  97.     //
  98.     //    Tests all key maps to see whether the key corresponding to
  99.     //    the given key code is down. Pass a value from 0 to 127; higher
  100.     //    values produce false.
  101.     //
  102.  
  103. pascal Boolean AKS_IsKeyDownAnywhere (unsigned char keyCode)
  104. {
  105.     if (keyCode <= kMaxKeyCode)
  106.     {
  107.         tKeyboardInfo *scan = gKeyboardInfoList;
  108.         while (scan)
  109.         {
  110.             if (scan->virtualKeyMap [keyCode] > 0)
  111.                 return true;
  112.     
  113.             scan = scan->next;
  114.         }
  115.     }
  116.  
  117.     return false;
  118. }
  119.  
  120.     //
  121.     //    EnsureHaveServiceRoutine
  122.     //
  123.     //    Ensures that the ADB service routine code resource has been loaded
  124.     //    into the global variable gServiceRoutine. The resource is loaded low
  125.     //    in the heap and locked. It stays loaded forever.
  126.     //
  127.     //    'mySetResLoad' is some cheap glue to make error-handling easier.
  128.     //
  129.  
  130. static pascal OSErr mySetResLoad (Boolean newLoadValue)
  131. {
  132.     SetResLoad (newLoadValue);
  133.     return ResError ( );
  134. }
  135.  
  136. static pascal OSErr EnsureHaveServiceRoutine (void)
  137. {
  138.     OSErr err = noErr;
  139.  
  140.     if (!gServiceRoutine)
  141.     {
  142.         Boolean oldResLoad = LMGetResLoad ( );
  143.         if (!oldResLoad || !(err = mySetResLoad (false)))
  144.         {
  145.             gServiceRoutine = Get1Resource ('AKSR',128);
  146.             if (!(err = ResError ( )))
  147.             {
  148.                 Size size = GetResourceSizeOnDisk (gServiceRoutine);
  149.                 if (!(err = ResError ( )))
  150.                 {
  151.                     ReserveMem (size);
  152.                     if (!(err = MemError ( )))
  153.                     {
  154.                         LoadResource (gServiceRoutine);
  155.                         if (!(err = ResError ( )))
  156.                         {
  157.                             HLock (gServiceRoutine);
  158.                             err = MemError ( );
  159.                         }
  160.                     }
  161.                 }
  162.  
  163.                 if (err)
  164.                 {
  165.                     ReleaseResource (gServiceRoutine);
  166.                     gServiceRoutine = nil;
  167.                 }
  168.             }
  169.  
  170.             if (oldResLoad)
  171.             {
  172.                 OSErr err2 = mySetResLoad (true);
  173.                 if (!err) err = err2;
  174.             }
  175.         }
  176.     }
  177.  
  178.     return err;
  179. }
  180.  
  181.     //
  182.     //    AKS_AcquireKeyboards
  183.     //
  184.     //    Walks the ADB device list and patches the service routine
  185.     //    of each keyboard so that an extra (internal) key map is maintained.
  186.     //    A block of memory is allocated via NewPtr for each keyboard.
  187.     //
  188.     //    You will probably want to call this function when your app
  189.     //    starts and on resume events.
  190.     //
  191.  
  192. pascal OSErr AKS_AcquireKeyboards (void)
  193. {
  194.     OSErr err = EnsureHaveServiceRoutine ( );
  195.  
  196.     if (!err)
  197.     {
  198.         ADBSetInfoBlock        siBlock;
  199.         ADBDataBlock        adbInfo;
  200.     
  201.         short adbCount = CountADBs ( );
  202.     
  203.         while (adbCount)
  204.         {
  205.             ADBAddress adbAddress = GetIndADB (&adbInfo, adbCount);
  206.     
  207.             if (adbAddress < 0)
  208.             {
  209.                 DebugStr ("\p adbAddress < 0 ?");
  210.                 err = paramErr;
  211.                 break;
  212.             }
  213.     
  214.             if (adbInfo.origADBAddr == 2) // 2 == keyboard
  215.             {
  216.                 //
  217.                 //    allocate a new record to track old service proc addresses
  218.                 //
  219.     
  220.                 tKeyboardInfo *newKeyboardInfo =
  221.                     (tKeyboardInfo *) NewPtrClear (sizeof (tKeyboardInfo));
  222.                 err = MemError ( );
  223.                 if (err) break;
  224.     
  225.                 //
  226.                 //    fill in the new record and (get ready to) push it into our list
  227.                 //
  228.  
  229.                 err = GetKMAP (newKeyboardInfo, adbInfo.devType);
  230.                 if (err)
  231.                 {
  232.                     DisposePtr ((Ptr) newKeyboardInfo);
  233.                     break;
  234.                 }
  235.  
  236.                 newKeyboardInfo->dbDataAreaAddr        = adbInfo.dbDataAreaAddr;
  237.                 newKeyboardInfo->dbServiceRtPtr        = adbInfo.dbServiceRtPtr;
  238.                 newKeyboardInfo->next                = gKeyboardInfoList;
  239.  
  240.                 //
  241.                 //    replace the device's service routine and data pointer
  242.                 //
  243.         
  244.                 siBlock.siService        = (ADBServiceRoutineUPP) *gServiceRoutine;
  245.                 siBlock.siDataAreaAddr    = (Ptr) newKeyboardInfo;
  246.     
  247.                 err = SetADBInfo (&siBlock, adbAddress);
  248.                 if (err)
  249.                 {
  250.                     DisposePtr ((Ptr) newKeyboardInfo);
  251.                     break;
  252.                 }
  253.     
  254.                 //
  255.                 //    push the new record into our list
  256.                 //
  257.     
  258.                 gKeyboardInfoList = newKeyboardInfo;
  259.             }
  260.     
  261.             --adbCount;
  262.         }
  263.     }
  264.  
  265.     return err;
  266. }
  267.  
  268.     //
  269.     //    AKS_RelinquishKeyboards
  270.     //
  271.     //    Walks the private internal list of patched ADB keyboard
  272.     //    service routines and unpatches them.
  273.     //
  274.     //    You will probably want to call this function when your app
  275.     //    quits and on suspend events. If you patch ExitToShell for
  276.     //    cleanup during emergency exits, you will want to call this
  277.     //    in your patch.
  278.     //
  279.  
  280. pascal OSErr AKS_RelinquishKeyboards (void)
  281. {
  282.     OSErr                err            = noErr;
  283.     short                adbCount    = CountADBs ( );
  284.     ADBDataBlock        adbInfo;
  285.  
  286.     while (gKeyboardInfoList)
  287.     {
  288.         tKeyboardInfo    *next        = gKeyboardInfoList->next;
  289.         short            index        = adbCount;
  290.  
  291.         while (index)
  292.         {
  293.             ADBAddress adbAddress = GetIndADB (&adbInfo, index);
  294.  
  295.             if (adbAddress < 0)
  296.             {
  297.                 DebugStr ("\p adbAddress < 0 ?");
  298.                 err = paramErr;
  299.                 break;
  300.             }
  301.  
  302.             if (adbInfo.dbDataAreaAddr == (Ptr) gKeyboardInfoList)
  303.             {
  304.                 ADBSetInfoBlock siBlock;
  305.  
  306.                 siBlock.siService        = gKeyboardInfoList->dbServiceRtPtr;
  307.                 siBlock.siDataAreaAddr    = gKeyboardInfoList->dbDataAreaAddr;
  308.     
  309.                 err = SetADBInfo (&siBlock, adbAddress);
  310.                 break;
  311.             }
  312.  
  313.             --index;
  314.         }
  315.  
  316.         DisposePtr ((Ptr) gKeyboardInfoList);
  317.         gKeyboardInfoList = next;
  318.     }
  319.  
  320.     return err;
  321. }
  322.